class_name CostUtil

## Cost工具类
## 每有一种特殊的Cost类型，都要在dict中注册，并且实现其check和do方法，否则用默认的

# Check函数
static var check_func_dict:Dictionary = {
	"Health" : check_cost_health,
	"Expression" : check_cost_expression,
}
# Do函数
static var do_func_dict:Dictionary = {
	"Health" : do_cost_health,
	"Expression" : do_cost_expression,
}

#region CheckCost
static func check_cost(entity:Entity,cost:Cost_Single) -> bool:
	if check_func_dict.has(cost.cost_type):
		return check_func_dict[cost.cost_type].call(entity,cost)
	else:
		#var error = "没有CostType：[{0}]对应的Check方法".format([cost.cost_type])])
		#push_error(error)
		var cur_cost = entity.entity_data.get_cost(cost.cost_type)
		if cur_cost == null:
			return false
		if cost.cost_value.property_type == CompositeProperty.PropertyType.Int and cost.cost_value.get_value(entity) < 0 and cur_cost >= 0:
			return cur_cost >= abs(cost.cost_value.get_value(entity))
		if cost.cost_value.property_type == CompositeProperty.PropertyType.Float and cost.cost_value.get_value(entity) < 0 and cur_cost >= 0:
			return cur_cost >= abs(cost.cost_value.get_value(entity))
		if cost.cost_value.property_type == CompositeProperty.PropertyType.EntityData and cost.cost_value.get_value(entity) < 0 and cur_cost >= 0:
			return cur_cost >= abs(cost.cost_value.get_value(entity))
		return true
static func check_cost_health(entity:Entity,cost:Cost_Single) -> bool:
	var cur_cost = entity.entity_data.get_cost(cost.cost_type)
	var max_health = entity.entity_data.get_attribute_value("MaxHealth")
	if cost.cost_value.get_value(entity) < 0 and cur_cost < cost.cost_value.get_value(entity):
		return false
	elif cost.cost_value.get_value(entity) >= 0 and cur_cost + cost.cost_value.get_value(entity) >= max_health:
		return false
	return true
static func check_cost_expression(entity:Entity,cost:Cost_Expression) -> bool:
	return evaluate_expression(entity,cost.check_expression,cost.expression_properties,false)
#endregion
#region DoCost
static func do_cost(entity:Entity,cost:Cost_Single,args:Dictionary = {}) -> bool:
	if do_func_dict.has(cost.cost_type):
		return do_func_dict[cost.cost_type].call(entity,cost,args)
	else:
		#var error = "没有CostType：[{0}]对应的Do方法".format([str(Utils.get_enum_key_by_value(GameEnum.CostType,cost.cost_type))])
		#push_error(error)
		entity.entity_data.set_cost(cost.cost_type,cost.cost_value.get_value(entity))
		return true
static func do_cost_health(entity:Entity,cost:Cost_Single,args:Dictionary = {}) -> bool:
	var cur_cost = entity.entity_data.get_cost(cost.cost_type)
	var max_health = entity.entity_data.get_attribute_value("MaxHealth")
	var diff = max_health - cur_cost
	var target_cost_value
	if cost.cost_value.get_value(entity) >= 0:
		target_cost_value = min(diff,cost.cost_value.get_value(entity))
	else:
		target_cost_value = -clamp(abs(cost.cost_value.get_value(entity)),0,cur_cost)
	var combat_component:CombatComponent = entity.get_component(CombatComponent)
	if combat_component:
		var hurt_box:HurtBox = combat_component.get_hurt_box()
		if hurt_box:
			hurt_box.hurt(HurtMessage.new(args.get("Source",null),entity,GameEnum.HurtMessageType.Hurt if target_cost_value < 0 else GameEnum.HurtMessageType.Recover,abs(target_cost_value)))
	#entity.entity_data.set_cost(cost.cost_type,target_cost_value)
	return true
static func do_cost_expression(entity:Entity,cost:Cost_Expression) -> bool:
	return evaluate_expression(entity,cost.do_expression,cost.expression_properties,false)
#endregion
#region 辅助功能函数
static func evaluate_expression(entity:Entity,expression:String,expression_properties:Dictionary,default_value:Variant) -> Variant:
	var ex = Expression.new()
	var input_names = expression_properties.keys()
	var result = ex.parse(expression,input_names)
	if result != OK:
		push_error("解析Cost{0}错误：{1}".format([expression,ex.get_error_text()]))
		return default_value
	var input_values:Array = []
	for input_name in input_names:
		input_values.append(expression_properties[input_name])
	var excute_result = ex.execute(input_values,entity,false)
	if ex.has_execute_failed():
		push_error("执行Cost{0}错误：{1}".format([expression,ex.get_error_text()]))
		return default_value
	return excute_result
#endregion
